﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using System.Net;
using Utils;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;

namespace FbScraper
{
	public class UserScraper
	{
		ILog logger = LogManager.GetLogger("UserScraper");
		// regex to catch all idies
		Regex reg = new Regex("(?<=\"id\":)[^,]*");

		Regex count = new Regex("(?<=\"count\":)\\d*");

		public int CollectedFriendsNum { get; set; }

		int? _freindsNum;
		//the total number of friends as reported by the ajax call
		public int TotalFreindsNum 
		{
			get
			{
				if (_freindsNum.HasValue)
					return _freindsNum.Value;
				return 0;
			}
			set
			{
				_freindsNum = value;
			}
		}
		private void SetTotalFreindsNum(string resText)
		{
			if (!_freindsNum.HasValue)
			{
				int temp = 0;
				int.TryParse(count.Match(resText).Value, out temp);
				TotalFreindsNum = temp;
			}
		}

		public  List<long> GetFriendsIDs(long userID, CookieContainer loginCookies)
		{
			List<long> Idies = new List<long>();

			Requester req = new Requester(userID, loginCookies);

			// loop through to get all friends			
			do
			{
				if (logger.IsDebugEnabled)
					logger.DebugFormat("Started getting friends from page {0}", req.PageNum);
				
				string resText = req.PerformRequest();
				
				//in the first itteration get the total number of friends
				SetTotalFreindsNum(resText);
				
				// get all idies from json
				var matches = reg.Matches(resText);
			
				//IMPORTANT: if there are no more fans- return.
				if (matches.Count == 0)
					break;
			
				foreach (Match match in matches)
					Idies.Add(Convert.ToInt64(match.Value));
				// moving to next page
				
				if (logger.IsDebugEnabled)				
					logger.DebugFormat("finished getting friends from page {0}", req.PageNum);				
			}
			// if the last page did not have 100 users on it then it is the last page.
			while (Idies.Count < TotalFreindsNum);

			return Idies;
		}
		public List<user> GetFriendsObjects(long userID, CookieContainer loginCookies) 
		{
			List<user> friends = new List<user>();

			Requester req = new Requester(userID, loginCookies);

			// loop through to get all friends			
			do
			{
				if (logger.IsDebugEnabled)
					logger.DebugFormat("Started getting friends from page {0}", req.PageNum);

				string resText = req.PerformRequest();

				//in the first itteration get the total number of friends
				SetTotalFreindsNum(resText);

				resText = resText.Substring(resText.IndexOf('{'));
				JavaScriptSerializer sd = new JavaScriptSerializer();
				Result result = sd.Deserialize<Result>(resText);
				if (result == null || result.payload == null || result.payload.user_info == null)
					break;

				foreach (var item in result.payload.user_info)
				{
					try
					{
						friends.Add(item.Value);
					}
					catch (Exception ex)
					{
						logger.Error("The supporter id " + item.Value.id + " already exists", ex);

						//sometimes it brings the same supporter twice.
					}
				}



				if (logger.IsDebugEnabled)
					logger.DebugFormat("finished getting friends from page {0}", req.PageNum);
			}
			// if the last page did not have 100 users on it then it is the last page.
			while (friends.Count < TotalFreindsNum);

			CollectedFriendsNum = friends.Count;
			return friends;
		}

		public int GetFriendNum(long id, CookieContainer loginCookies)
		{
			logger.DebugFormat(" getting friends num for facebook id {0}" + id);
			string url = "http://www.facebook.com/profile.php?id=" + id;

			// browsing to profile page
			var resText = WebReq.GetWebResponse(url, 300000, false, loginCookies, null, null);

			var friendCount = new Regex("(?<=\\>)[\\d,]*(?=\\sfriends\\<)").Match(resText).Value.Replace(",", "");

			if (string.IsNullOrEmpty(friendCount))
			{
				try
				{
					resText = WebReq.GetWebResponse(new Regex("(?<=window\\.location\\.replace\\(\")http:\\\\/\\\\/www.facebook.com\\\\/[^\"]*").Match(resText).Value.Replace("\\", ""), 300000, false, loginCookies, null, null);
					friendCount = new Regex("(?<=\\>)[\\d,]*(?=\\sfriends\\<)").Match(resText).Value;
				}
				catch (Exception ex)
				{
					logger.Error("error getting friendsnum", ex);
				}
			}
			if (string.IsNullOrEmpty(friendCount))
				return 0;
			return Convert.ToInt32(friendCount.Replace(",",""));
		}

		class Requester
		{
			// this cannot be changed facebook sends 100 even if you request more
			const int numOfResults = 100;
			const string urlFormat = "http://www.facebook.com/profile.php?id={0}";
			const string callType = "FriendManager";

			CookieContainer loginCookies;
			string userUrl;
			long userID;			
			string formID;
			string fb_dtsg;
			
			public int PageNum { get; set; }

			public Requester(long uid, CookieContainer cookies)
			{
				loginCookies = cookies;
				userID = uid;
				userUrl = string.Format(urlFormat, userID);
				//initilze the page num with -1 so it will becone 0 on the fris request
				PageNum = -1;

				GetBaseValues();
			}

			private void GetBaseValues()
			{
				// brows to profile page
				var resText = WebReq.GetWebResponse(userUrl, 300000, false, loginCookies, null, null);

				// these values are needed for the next calls. i do not know what there meaning is.
				formID = new Regex("(?<=name=\"post_form_id\" value=\")[^\"]*").Match(resText).Value;
				fb_dtsg = new Regex("(?<=fb_dtsg:\")[^\"]*").Match(resText).Value;

				if (string.IsNullOrEmpty(formID))
				{
					try
					{
						resText = WebReq.GetWebResponse(new Regex("(?<=window\\.location\\.replace\\(\")http:\\\\/\\\\/www.facebook.com\\\\/[^\"]*").Match(resText).Value.Replace("\\", ""), 300000, false, loginCookies, null, null);
						formID = new Regex("(?<=name=\"post_form_id\" value=\")[^\"]*").Match(resText).Value;
						fb_dtsg = new Regex("(?<=fb_dtsg:\")[^\"]*").Match(resText).Value;

					}
					catch (Exception ex)
					{
						//logger.Error("error GetFriendsIDs", ex);
					}
				}
			}

			public string PerformRequest()
			{
				PageNum++; 
				var postData = string.Format("edge_type=everyone&page={4}&limit={3}&node_id={0}&class={5}&post_form_id={1}&fb_dtsg={2}&post_form_id_source=AsyncRequest",
					userID, formID, fb_dtsg, numOfResults, PageNum, callType);
				string resText;
				try
				{
					resText = WebReq.GetWebResponse("http://www.facebook.com/ajax/social_graph/fetch.php?__a=1", 300000, false, loginCookies, null, postData);
				}
				catch (Exception ex)
				{
					//logger.Error("error GetFriendsIDs2", ex);
					resText = string.Empty;
				}

				return resText;
			}
		}
	}
}
